package com.tiku.controller;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConfig;
import com.alipay.api.internal.util.AlipaySignature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tiku.entity.AlipayNotifyParam;
import com.tiku.entity.UserOrder;
import com.tiku.entity.UserOrderRelation;
import com.tiku.entity.UserPayTiku;
import com.tiku.service.UserOrderRelationService;
import com.tiku.service.UserOrderService;
import com.tiku.service.UserPayTikuService;
import org.mybatis.logging.Logger;
import org.mybatis.logging.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Controller
public class AlipayCallbackController {



    private ExecutorService executorService = Executors.newFixedThreadPool(20);

    @Value("${alipay.alipayPublicKey}")
    private String alipayPublicKey;

    @Autowired
    private UserOrderService userOrderService;

    @Autowired
    private UserOrderRelationService relationService;

    @Autowired
    private UserPayTikuService userPayTikuService;
    /**
     * <pre>
     * 第一步:验证签名,签名通过后进行第二步
     * 第二步:按一下步骤进行验证
     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
     * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
     * 3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。
     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
     * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
     * </pre>
     *
     * @param request
     * @return
     */
    @RequestMapping("/alipay_callback")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public String callback(HttpServletRequest request) {
        Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中
        String paramsJson = JSON.toJSONString(params);
        System.out.println("支付宝回调,"+ paramsJson);
        try {
            AlipayConfig alipayConfig = new AlipayConfig();// 支付宝配置
            alipayConfig.setAlipayPublicKey(alipayPublicKey);
            // 调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(),
                    alipayConfig.getCharset(), alipayConfig.getSignType());
            if (signVerified) {
                System.out.println("支付宝回调签名认证成功");
                // 按照支付结果异步通知中的描述，对支付结果中的业务内容进行1\2\3\4二次校验，校验成功后在response中返回success，校验失败返回failure
                this.check(params);
                // 另起线程处理业务
                AlipayNotifyParam param = buildAlipayNotifyParam(params);
                //获取支付状态
                String trade_status = param.getTradeStatus();
                //成功后
                payOkPost(params.get("out_trade_no"));

                // 如果签名验证正确，立即返回success，后续业务另起线程单独处理
                // 业务处理失败，可查看日志进行补偿，跟支付宝已经没多大关系。
                return "success";
            } else {
                System.out.println("支付宝回调签名认证失败，signVerified=false, paramsJson:"+ paramsJson);
                return "failure";
            }
        } catch (AlipayApiException e) {
            System.out.println("支付宝回调签名认证失败,paramsJson:"+paramsJson+",errorMsg:"+ e.getMessage());
            return "failure";
        }
    }

    //通过订单号来确认购买的题库， 并给用户添加指定题库的权限
    private void payOkPost(String out_trade_no) {
        //更新订单状态为已支付
        UserOrder one = userOrderService.getOne(new LambdaQueryWrapper<UserOrder>().eq(UserOrder::getOrderNumber, out_trade_no));
        one.setOrderType("1");
        userOrderService.updateById(one);

        //将用户购买的题库进行记录
        List<UserOrderRelation> list = relationService.list(new LambdaQueryWrapper<UserOrderRelation>()
                .eq(UserOrderRelation::getOrderNumber, out_trade_no));

        List<UserPayTiku> userPayTikuList=new ArrayList<>();
        for (UserOrderRelation userOrderRelation : list) {
            String specid = userOrderRelation.getSpecid();
            UserPayTiku userPayTiku = new UserPayTiku();
            userPayTiku.setPaySpecId(specid);
            userPayTiku.setUserId(one.getUserId());
            userPayTiku.setCreateTime(new Date());
            userPayTikuList.add(userPayTiku);
        }

        userPayTikuService.saveBatch(userPayTikuList);

    }

    // 将request中的参数转换成Map
    private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        Map<String, String> retMap = new HashMap<String, String>();

        Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();

        for (Map.Entry<String, String[]> entry : entrySet) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            int valLen = values.length;

            if (valLen == 1) {
                retMap.put(name, values[0]);
            } else if (valLen > 1) {
                StringBuilder sb = new StringBuilder();
                for (String val : values) {
                    sb.append(",").append(val);
                }
                retMap.put(name, sb.toString().substring(1));
            } else {
                retMap.put(name, "");
            }
        }

        return retMap;
    }

    private AlipayNotifyParam buildAlipayNotifyParam(Map<String, String> params) {
        String json = JSON.toJSONString(params);
        return JSON.parseObject(json, AlipayNotifyParam.class);
    }

    /**
     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
     * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
     * 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。
     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
     * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
     *
     * @param params
     * @throws AlipayApiException
     */
        private void check(Map<String, String> params) throws AlipayApiException {
            String outTradeNo = params.get("out_trade_no");

            // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
            UserOrder order = getOrderByOutTradeNo(outTradeNo); // 这个方法自己实现
            if (order == null) {
                throw new AlipayApiException("out_trade_no错误");
            }

            // 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
            long total_amount = new BigDecimal(params.get("total_amount")).longValue();
            if (total_amount != order.getMoney().longValue()) {
                throw new AlipayApiException("error total_amount");
            }

            // 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
            // 第三步可根据实际情况省略

            // 4、验证app_id是否为该商户本身。
//            if (!params.get("app_id").equals(alipayConfig.getAppid())) {
//            throw new AlipayApiException("app_id不一致");
//        }
    }

    private UserOrder getOrderByOutTradeNo(String outTradeNo) {
        UserOrder userOrder = userOrderService.getOne(new LambdaQueryWrapper<UserOrder>().eq(UserOrder::getOrderNumber, outTradeNo));
        return userOrder;
    }

}
